﻿#if STEAM_NETWORK
using Steamworks;
using System.Collections.Generic;


namespace Manox.Libs.Networking.Steam
{
    /// <summary>
    /// Steam network node.
    /// </summary>
    public class SteamNetworkNode : NetworkNode
    {
        /// <summary>Receive buffer length.</summary>
        private uint ReceiveBufferLength;
        /// <summary>Receive buffer.</summary>
        private byte[] ReceiveBuffer;
        /// <summary>Connections table.</summary>
        protected SortedDictionary<CSteamID, SteamNetworkConnectionId> ConnectionsTable;
        /// <summary>Message length.</summary>
        private uint MessageLength;
        /// <summary>Message sender.</summary>
        private CSteamID MessageSender;
        /// <summary>On session request.</summary>
        protected Callback<P2PSessionRequest_t> SessionRequestCallback;



        /// <summary>
        /// Awake.
        /// </summary>
        public override void Awake()
        {
            base.Awake();

            this.ReceiveBufferLength = 1024;
            this.ReceiveBuffer = new byte[this.ReceiveBufferLength];

            this.ConnectionsTable = new SortedDictionary<CSteamID, SteamNetworkConnectionId>();            
        }

        /// <summary>
        /// Start.
        /// </summary>
        public override void Start()
        {
            base.Start();      
        }

        /// <summary>
        /// On destroy.
        /// </summary>
        public override void OnDestroy()
        {
            base.OnDestroy();
        }

        /// <summary>
        /// On enable.
        /// </summary>
        public override void OnEnable()
        {
            base.OnEnable();

            this.SessionRequestCallback = Callback<P2PSessionRequest_t>.Create(this.OnSessionRequest);
        }

        /// <summary>
        /// Update.
        /// </summary>
        public override void FixedUpdate()
        {
            base.FixedUpdate();            

            while (SteamNetworking.IsP2PPacketAvailable(out this.MessageLength))
            {
                if (this.ReceiveBufferLength < this.MessageLength)
                {
                    this.ReceiveBufferLength = this.MessageLength;

                    System.Array.Resize<byte>(ref this.ReceiveBuffer, (int)this.ReceiveBufferLength);
                }

                uint readedLenght;                

                if (SteamNetworking.ReadP2PPacket(this.ReceiveBuffer, this.MessageLength, out readedLenght, out this.MessageSender))
                {
                    NetworkMessage message = MessagePacker.Unpack(this.ReceiveBuffer);

                    if (message != null)
                        this.SendMessageToListeners(this.GetConnection(this.MessageSender), this.ReceiveBuffer[0], message);
                }
            }
        }

        /// <summary>
        /// On session request.
        /// </summary>
        /// <param name="pCallback"></param>
        private void OnSessionRequest(P2PSessionRequest_t pCallback)
        {
            SteamNetworking.AcceptP2PSessionWithUser(pCallback.m_steamIDRemote);
        }

        /// <summary>
        /// Send data.
        /// </summary>
        /// <param name="connection"></param>
        /// <param name="data"></param>
        /// <param name="dataLength"></param>
        public override void Send(NetworkConnectionId connection, byte[] data, int dataLength)
        {
            if (!SteamNetworking.SendP2PPacket((connection as SteamNetworkConnectionId).ConnectionId,
                data, (uint)dataLength, EP2PSend.k_EP2PSendReliable))
                UnityEngine.Debug.LogError("Steam network node : Can't send packet !");
        }

        /// <summary>
        /// Get connection.
        /// </summary>
        /// <param name="steamId"></param>
        /// <returns></returns>
        private SteamNetworkConnectionId GetConnection(CSteamID steamId)
        {
            if (!this.ConnectionsTable.ContainsKey(steamId))
                this.ConnectionsTable.Add(steamId, new SteamNetworkConnectionId(steamId));

            return this.ConnectionsTable[steamId];
        }
    }
}
#endif